Skip to content

Enhancement/Expansion of TLG Catalog#1356

Open
h5hoang wants to merge 52 commits into
mainfrom
1343-enhancement-expand-tlg-catalog
Open

Enhancement/Expansion of TLG Catalog#1356
h5hoang wants to merge 52 commits into
mainfrom
1343-enhancement-expand-tlg-catalog

Conversation

@h5hoang

@h5hoang h5hoang commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Issue

Closes #1343

Description

Expands the TLG catalog with new table, graph, and listing functions for PK reports, following the insightsengineering TLG catalog specifications.

New TLG functions (26 new entries in tlg.yaml):

  • Tables (9): t_pkct01 (summary concentration table with 4 variants: by TRT, by dose, TAD, dose+TAD), t_pkpt03_col (summary PK parameters), t_pkpt03_MP_col (metabolite/parent ratios), t_pkpt07_norm (dose-normalized parameters), t_pkpt08_uri (urine amount/percent recovered), t_pkpt11_gmr (geometric mean ratio with CIs)
  • Graphs (12): g_pkcg01/02/03_sbs (side-by-side concentration plots), p_pkcg03_lin/log/sbs_dose (mean concentration by dose), p_pkpg01_cum/per (urine recovery profiles), p_pkpg02_doseprop (dose-proportionality scatter with power-model regression), p_pkpg03_boxp / p_pkpg04_boxp (PK parameter boxplots), p_pkpg06_mp (metabolite/parent ratio boxplots)
  • Listings (5): l_pkcl01_tad (concentration listing by TAD), l_pkcl02_uri (urine concentration listing), l_pkpl01 (individual PK parameters), l_pkpl01_mp (metabolite ratios), l_pkpl04_mp (treatment comparison)

Shared helpers (R/utils-tlg.R):

  • split_and_apply — splits data by grouping variables and applies a function to each subset (used by all new TLG functions)
  • filter_metabolite_rows — three-tier metabolite detection fallback (METABFL → PPCAT → PARAM)
  • .summarise_adpp — descriptive statistics for PK parameter values (n, Mean, SD, CV%, GeoMean, GeoCV%, Median, Min, Max)
  • .build_pkpp_table — deduplicates ADPP rows and applies summary statistics per stratum/parameter
  • .get_var_label — label attribute lookup with column-name fallback

Shiny wiring improvements:

  • Added type = "table" support to tlg_module_server / tlg_module_ui (tables render via reactable)
  • ADPP data now flows from tab_nca_servertab_tlg_server → modules via a dedicated adpp reactive, avoiding recomputation
  • filter_tlg_excluded applied once at the tab_tlg boundary (not per-module), and extended to handle ADPP's PPSUMFL flag alongside ADNCA's PKSUM1F
  • Fixed NA handling in filter_tlg_excluded — rows with NA in exclusion flag columns were silently dropped; now correctly preserved
  • Replaced random module ID suffixes with deterministic IDs + a registration environment to prevent duplicate Shiny observer accumulation on re-submit
  • Extracted .build_tlg_panels helper to eliminate copy-paste across table/graph/listing renderUI blocks

Other changes:

  • export_cdisc: carries DOSEA/DOSEU columns through to ADPP for dose-proportionality TLGs
  • Fixed g_pkcg02_sbs YAML entry pointing to g_pkcg02_log instead of g_pkcg02_sbs
  • Removed stray print(tlg_order()) debug statement in tab_tlg.R

Definition of Done

  • List of proposed new TLGs discussed and agreed
  • New graph functions implemented and registered in tlg.yaml
  • New table functions implemented and registered in tlg.yaml
  • Each new function has unit tests
  • All new TLGs render correctly in the app

How to test

  1. Run NCA on a dataset that includes multiple treatment arms and at least one analyte
  2. Navigate to the TLG tab, the new Tables sub-tab should be functional (previously a placeholder)
  3. Submit the default TLG selection and verify tables render as reactable widgets with pagination
  4. Enable non-default entries (dose-proportionality, urine, GMR, boxplots) and verify they render without error
  5. For ADPP-based TLGs: confirm that data reflects the NCA results (not stale or recomputed)
  6. Re-submit the TLG order multiple times, verify no duplicate observer accumulation (pagination buttons should fire exactly once per click)
  7. For urine TLGs (t_pkpt08_uri, p_pkpg01_cum/per, l_pkcl02_uri): test with a dataset containing urine specimens; verify the condition: "URINE" auto-selects these entries when urine data is present (don't forget to click urine in NCA set-up tab like me :'))

Contributor checklist

  • Code passes lintr checks
  • Code passes all unit tests
  • New logic covered by unit tests
  • New logic is documented
  • App or package changes are reflected in NEWS
  • Package version is incremented
  • R script works with the new implementation (if applicable)
  • Settings upload works with the new implementation (if applicable)
  • If any .scss change was done, run data-raw/compile_css.R
  • If a package dependency was added/changed, run data-raw/test_suggests_hidden.R

Notes to reviewer

  • All new graph functions use direct ggplot2 (no tern dependency), per Enhancement: Remove tern dependency from TLG plots #1319.
  • The t_pkct01 summary function has its own .summarise_group rather than reusing .summarise_adpp. This is intentional because concentration tables have BLQ-specific semantics (BLQ counting, different n definition that includes BLQ subjects) that don't map cleanly to the ADPP helper.
  • The condition auto-selection for ADPP-based TLGs (e.g. t_pkpt08_uri with condition: "URINE") evaluates against ADNCA's PCSPEC column, not ADPP's PPSPEC. This works in practice (if ADNCA has urine specimens, ADPP should too) but could be tightened in a follow-up if needed.
  • The DOSEA column is now carried through export_cdisc for dose-proportionality graphs (p_pkpg02_doseprop, p_pkcg03_*_dose). If DOSEA is absent from the source data, these TLGs will show an error message at render time (caught by tryCatch in tlg_module_server). They are all is_default: false so they won't be pre-selected.

h5hoang added 23 commits June 5, 2026 11:51
@h5hoang h5hoang linked an issue Jun 18, 2026 that may be closed by this pull request
5 tasks
@h5hoang h5hoang marked this pull request as ready for review June 22, 2026 20:33
@KOBANAK

KOBANAK commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Hi, thanks for all the work on this.

I wanted to share a few observations regarding the summary tables so far.
These are mostly around ordering and presentation.
I confirm the tables render as reactable widgets and pagination works.

  • When a table is split by parameter and specimen (PARAM × PCSPEC), the resulting tables are stacked without a title, so it's not immediately clear which analyte/specimen each one corresponds to. It would be really helpful to display the group as a header above each table.

  • Looking at the PK Summary Conc by TRT for example, the tables should be organized by TRT01A, then by ordered ATPTREF and NFRLT, so that all rows for a given treatment arm are grouped together. At the moment the same TRT01A values reappear further down the table. Grouping each arm with its timepoints ordered would make the table much easier to read.

  • It would be great to have readable labels for the statistic columns instead of the programming names ( GeoMean → "Geometric Mean", GeoCV_pct → "Geometric CV%", CV_pct → "CV%" etc..).

I'll continue going through the rest and follow up with anything else I find.
Thanks again :)

@Gero1999

Copy link
Copy Markdown
Collaborator

hey @h5hoang if possible give a look to @KOBANAK feedback. Feel free to also keep the discussion thread if you feel that something is not worthy or better for later. I will try to give a look to this later today as well

Comment thread R/l_pkcl01.R Outdated
Comment on lines +284 to +290
urine_specs = c("URINE", "Urine"),
listgroup_vars = c("PARAM", "PCSPEC"),
displaying_vars = NULL,
...
) {
if ("PCSPEC" %in% names(data)) {
data <- data[data$PCSPEC %in% urine_specs, , drop = FALSE]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: The expected value is URINE based on CDISC, but you can be more protective than this:

Suggested change
urine_specs = c("URINE", "Urine"),
listgroup_vars = c("PARAM", "PCSPEC"),
displaying_vars = NULL,
...
) {
if ("PCSPEC" %in% names(data)) {
data <- data[data$PCSPEC %in% urine_specs, , drop = FALSE]
urine_specs = c("URINE"),
listgroup_vars = c("PARAM", "PCSPEC"),
displaying_vars = NULL,
...
) {
if ("PCSPEC" %in% names(data)) {
data <- data[toupper(data$PCSPEC) %in% urine_specs, , drop = FALSE]

@h5hoang

h5hoang commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator Author

Thank you both for the feedback! I really appreciate it a lot 😄

I've addressed everything raised from the urine specimen filters now matching case-insensitively to making the summary tables more readable (split tables show a group header, rows are grouped by treatment arm and ordered by timepoint, and the statistic columns now use readable labels instead of the raw names). I've also added in new unit tests to cover these changes as well

please let me know if there are any other changes/feedback you guys might have! thanks!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enhancement: Expand TLG catalog with new graph and table functions

3 participants